import pygame.transform
from pygame.locals import RLEACCEL
from common import Tick,BounderyVal
from math import atan,sin,cos,pi,sqrt,hypot
from gameobjects import GameObject,vel_limit
from weapons import Shot

class Ship(GameObject):
    create_sound = None
    thrust_loop_sound = None
    thrust_start_sound = None
    thrust_end_sound = None
    explode_sound = None
    
    def __init__(self,game_engine,pos_x,pos_y,vel_x,vel_y,image=None,id = None,owner=None):
        GameObject.__init__(self,game_engine,pos_x,pos_y,vel_x,vel_y,image,id=id)
        self.sp = game_engine.soundplayer

        self.org_image = self.image # Un rotated org ship surface
        self.angchange = 0 
        self.owner = owner
        self.ang = 0
        self.old_ang = self.ang
        self.thrust = 0
        self.max_vel = 450*game_engine.relative_speed
        self.cur_tile_pos = [0,0]
        self.landing_ang = 0.3    # Maximal landing ang in radians  
        self.life = BounderyVal(0,50,50)    # life points
        self.fuel = BounderyVal(0,100,200)    # fule points
        self.max_fuel = 150.0
        self.last_hit_by = None # used to give credit to the last player that hit me I die.
        self.collide = 0

        self.ship_mass = 480 # ship chassi mass
        self.mass = 0 # ship total mass
        self.changed = 0
        self.old_crashed = 0
        self.crashed = 0
        self.landed = False
        deg = round(((self.ang/pi)*(180))/6)*6
        self.image = pygame.transform.rotate(self.org_image, deg).convert()
        self.image.set_colorkey(self.org_image.get_colorkey(), RLEACCEL)
        self.rect = self.image.get_rect()
        self.rect.center = (self.pos_x,self.pos_y)
        
        # thrust sound vars
        self.thrust_channel = None
        self.thrust_changed = 0
        self.playing_thrust = 0
    
        # weapons
        self.prim_weapon = None
        self.sec_weapon = None

    # Set velocity 
    def setThrust(self, thrust,dt):
        'Sets engine thrust and keeps speed limit'
        if self.fuel.val > 0:
            self.fuel.change(-(dt*5))
            self.thrust = (thrust/self.mass)*50
        else:
            # no fuel but let ship move with less thrust...
            self.thrust = (thrust/self.mass)*25
        ang = self.ang
        vx,vy = self.vel_x,self.vel_y
        vx += self.thrust*-sin(ang)*dt
        vy += self.thrust*-cos(ang)*dt

        self.vel_x,self.vel_y = vel_limit(vx,vy,self.max_vel)
            
        self.thrust_changed = 1

            

    def reset(self):
        GameObject.reset(self)
        self.ang = 0
        self.old_ang = 1 #ang!=old_ang to get sprite recalculated
        self.thrust = 0
        self.landed = False
        self.crashed = 0
        self.old_crashed = 0
        self.old_collide = False

        
        self.life.reset()
        self.fuel.reset()
        self.prim_weapon.ammo.reset()
        self.sec_weapon.ammo.reset()
        self.last_hit_by = None
        self.changed = 0
        self.thrust_changed = 0
        if self.playing_thrust:
            try:
                self.thrust_channel.stop()
            except:
                pass
            self.playing_thrust = 0

    def changePrimAmmo(self,dammo):
        changed = self.prim_weapon.ammo.change(dammo)
        self.changed |= changed
        return changed

    def changeSecAmmo(self,dammo):
        changed = self.sec_weapon.ammo.change(dammo)
        self.changed |= changed
        return changed

    def changeLife(self,dlife):
        ' Changes life points, die if life < 0'
        changed = self.life.change(dlife)
        self.changed |= changed
        return changed

    def changeFuel(self,dfuel):
        ' Changes life points, die if life < 0'
        changed = self.fuel.change(dfuel)
        self.changed |= changed 
        return changed
        
    # Set position 
    def pos(self, x, y):
        'float x, float y'
        self.pos_x = x
        self.pos_y = y
        self.rect.center = (self.pos_x,self.pos_y)

    # Set position 
    def moveAng(self, rad, dt):
        'float x, float y'
        self.old_ang = self.ang
        self.ang += rad*dt

    # Set position 
    def changeAng(self, rad, dt):
        ' changes ang, this function uses acceleration when changing ang to allow for finer control'
        self.old_ang = self.ang
        rd,rdmax = self.angchange,rad
        # check if direction changed
        if rd > 0 and rad < 0:    rd = 0.0
        elif rd < 0 and rad > 0:rd = 0.0
        # acc ang change
        rd +=rad*0.12
        # limit ang change to max change
        if rdmax > 0:
            if rd > rdmax: rd=rdmax
        elif rdmax < 0:
            if rd < rdmax: rd=rdmax

        self.ang += rd*dt
        self.angchange = rd

    def update(self,dt=0):
        self.prim_weapon.update(dt)
        self.sec_weapon.update(dt)

        # Play or stop thrust sound
        if self.thrust_changed and not self.playing_thrust:
            self.thrust_channel = self.sp.play(self.thrust_loop_sound,-1,1)
            self.sp.play(self.thrust_start_sound,0,1)
            self.playing_thrust = 1
        elif not self.thrust_changed and self.playing_thrust:
            self.playing_thrust = 0
            try:
                self.thrust_channel.stop()
                self.thrust_channel.queue(self.thrust_end_sound)
                #self.sp.play(self.thrust_end_sound,0,1)
            except:
                pass

        # reset thrust changed
        self.thrust_changed = 0
        self.mass =self.ship_mass + self.fuel.val/20 + self.prim_weapon.mass*0.03 + self.sec_weapon.mass*0.05

        if dt:
            self.move(dt)


        #rotate ship picture
        if self.ang != self.old_ang:
            deg = round(((self.ang/pi)*(180))/6)*6
            image = pygame.transform.rotate(self.org_image, deg)
            del self.image
            self.image = image.convert()
            del image
            #self.image.set_colorkey(self.org_image.get_colorkey(), RLEACCEL)
            self.rect = self.image.get_rect()
            self.old_ang = self.ang
        else:
            self.angchange = 0

        # set my rect so that self.pos reflects the center of the rect
        self.rect.center = (self.pos_x,self.pos_y)
            
        #self.crashed = self.collide and not self.landed
        
    def checkLanded(self):
        if self.collide_dirs == self.COLLIDE_BOTTOM:
            is_in_landing_ang = ((self.ang%(pi*2)) < self.landing_ang or (-(self.ang)%(pi*2)) < self.landing_ang)
            if is_in_landing_ang: 
                self.landed = True
                self.vel_x = 0
                self.vel_y = 0
                self.ang = 0
                self.old_ang = self.ang+1 # ugly hack to make ang and old_ang differ to update ship pic
            else:
                self.landed = False
        else:
            self.landed = False
        self.old_collide = self.collide
            
        
    def move(self, dt=0):
        ' Update animation (apply the velocity*time, that is)'
        super(Ship,self).move(dt)
#        self.landed = self.collide_dirs == self.COLLIDE_BOTTOM and self.hasLanded()


        
    def explode(self):
        vel = 300
        rad_deg = pi/180
        self.sp.play(self.explode_sound,0,1)
        addShot = self.game_engine.addShot
        # create a cluster of Shots
        for deg in range(0,360,16):
            dir_x,dir_y = (sin(deg*rad_deg), cos(deg *rad_deg) )
            addShot(Shot(self.game_engine,self.rect.center[0],self.rect.center[1],
                         self.vel_x+vel*dir_x,self.vel_y+vel*dir_y,
                         power=3,owner_id=None), local=0,nosound=1) # local=0 dont tell clients
            

    def kill(self):
        'make ship explode if it is killed '
        self.explode()
        if self.thrust_channel: self.thrust_channel.stop()
        GameObject.kill(self)


    def sync(self,sync_info):
        ' Sync myself with sync dict '
        GameObject.sync(self,sync_info)
        self.ang = sync_info['ang']
        self.life.val = sync_info['life']
        self.fuel.val = sync_info['fuel']
        self.prim_weapon.ammo.val = sync_info['pammo']
        self.sec_weapon.ammo.val = sync_info['secammo']

    def getSync(self):
        ' Return information about GameObject for syncing over net '
        sync_hash = GameObject.getSync(self)
        sync_hash['ang']=self.ang
        sync_hash['life']=self.life.val
        sync_hash['fuel']=self.fuel.val
        sync_hash['pammo'] = self.prim_weapon.ammo.val
        sync_hash['secammo'] = self.sec_weapon.ammo.val
        return sync_hash
